from typing import Dict, Set
from commandsniffer.graph import CommandGraph
from commandsniffer.commands.base import BaseCommand

import os


class GraphPass:
    def run(self, graph: CommandGraph) -> None:
        pass


class RefereshGraphPass(GraphPass):
    def full_path(self, path: str, command: BaseCommand) -> str:
        if os.path.isabs(path):
            return os.path.normpath(path)
        return os.path.normpath(os.path.join(command.working_directory, path))

    def find_cycle(self, graph: CommandGraph, command: BaseCommand, pending: Set[BaseCommand]) -> Set[BaseCommand]:
        if command in pending:
            return pending
        for dep_command in graph.dependencies.get(command, set()):
            cycle = self.find_cycle(graph, dep_command, pending | set([command]))
            if cycle:
                return cycle
        return set()

    def run(self, graph: CommandGraph) -> None:
        output_commands: Dict[str, BaseCommand] = {}
        for command in graph.commands.values():
            if command.output_file is not None:
                output_commands[self.full_path(command.output_file, command)] = command

        # command A depends on command B if A has an input file which is the output file of command B
        for command in graph.commands.values():
            for input_file in command.input_files:
                input_file = self.full_path(input_file, command)
                if input_file in output_commands:
                    graph.dependencies[command].add(output_commands[input_file])
            # remove self-dependency
            graph.dependencies.get(command, set()).discard(command)

        # detect circular dependencies
        for command in graph.commands.values():
            cycle = self.find_cycle(graph, command, set())
            if cycle:
                print(f"Circular dependency detected: {cycle}")
                for cmd in cycle:
                    print(f"  {cmd.command} {' '.join(cmd.arguments)}")
                raise ValueError("Circular dependency found")
